Loading libraries

library(scran)
library(scater)
library(HDF5Array)
library(ggplot2)
library(Matrix)
library(pheatmap)
library(RColorBrewer)
library(uwot)
library(grid)
library(gridExtra)
library(GEDI)

Directories

dir_data<- "../data_objects/"
dir_data_hdf5<- paste0(dir_data, "COVID19_SCE/")

Functions

#' Plot 2D embedding 
#'
#' Plot a 2D representation (embedding) of cells
#' @param embedding_mat Embedding
#' @param colour vector of variable to plot
#' @param randomize Logical. Whether to randomize data before plotting.
#'
#' @return ggplot2 object
#' @export
#'
plot_embedding <- function(embedding_mat,colour,randomize=T, size_point=0.05) {
  # create a data frame that will have the embedding as well as the colors
  embedding_obj <- data.frame(
    Dim1=embedding_mat[,1],
    Dim2=embedding_mat[,2],
    Var=colour )

  # randomize the order of the objects
  if( randomize ) {
    embedding_obj <- embedding_obj[ sample.int(nrow(embedding_obj)), ]
  }
  # create the plots
  if(is.numeric(colour)) # the color variable is numeric
  {
    #embedding_obj$Var <- embedding_obj$Var - mean(embedding_obj$Var)
    lim <- stats::quantile(abs(embedding_obj$Var),0.99)
    ggplot2::ggplot(
      embedding_obj, ggplot2::aes_string( x="Dim1", y="Dim2", colour="Var"))+
      ggplot2::geom_point(size=size_point)+
      ggplot2::theme_minimal()+
      ggplot2::scale_color_gradientn( limits=c(-lim,lim), colours=c("blue","light grey","red"), oob=scales::squish )
  } else {
    ggplot2::ggplot(
      embedding_obj, ggplot2::aes_string( x="Dim1", y="Dim2", colour="Var"))+
      ggplot2::geom_point(size=size_point)+
      ggplot2::theme_minimal()+
      ggplot2::guides(colour=ggplot2::guide_legend(override.aes=list(size=3)))

  }
}

summary_markers<- function(markers_sce){
    lis_markers<- lapply(names(markers_sce), function(cluster_look) {
        temp_df<- markers_sce[[cluster_look]]
        temp_df$Gene<- rownames(temp_df)
        temp_df$cluster<- cluster_look
        # Checking if an FDR is equal to 0
        if( any(temp_df$FDR == 0) ){
            min_pvalue<- sort(unique(temp_df$FDR))[2] # take the second lowest pvalue
            cat("pvalues equal to 0 have been changed to:", min_pvalue, "\n")
            temp_df$FDR[temp_df$FDR == 0] <- min_pvalue # setting lower bound
        }
        temp_df$neglog_FDR<- -log10(temp_df$FDR)
        temp_df<- temp_df[sort(rownames(temp_df)),]
        data.frame(temp_df[,c("Gene", "cluster", "p.value", "FDR", "neglog_FDR")])
    })
    names(lis_markers)<- names(markers_sce)    
    return(lis_markers)    
}

pheatmap.colorsymmetric <- function(x,lim=NULL,...)
{
    require(pheatmap)
    if(is.null(lim) ){
        lim <- max(abs(x), na.rm=TRUE)
    }
    if( min(x, na.rm=TRUE) < 0 ){
        lim_down<- -lim
        col_palette<- colorRampPalette(c("blue","white","red"))(256)
    }else{        
        lim_down<- 0
        col_palette<- colorRampPalette(c("white","red"))(256)
    }
    pheatmap(
        x, color = col_palette,
        breaks=seq(lim_down,lim,length.out=255), ... )
}

Loading data

# Reading SCE object
sce<- loadHDF5SummarizedExperiment(dir=dir_data_hdf5)

Loading GEDI model

meta<- data.frame(colData(sce))

# Reading GEDI model 
model<- readRDS(paste0(dir_data, "COVID19_gedi_model_cohort1_TF.rds"))
    
# reorder meta based on GEDI order
meta<- meta[model$aux$cellIDs,]

# Get activities per cell
ADB<- getADB.gedi(model)

# Get ZDB
ZDB<- getZDB.gedi(model)

# Get the gradient for all TFs
gradients <- getActivityGradients.gedi( model )

# Now, retrieve the differential gene expression per cell ( severe vs control)
t( model$aux$inputH)
            (Intercept) group_per_samplemild group_per_samplesevere
C19-CB-0001           1                    1                      0
C19-CB-0003           1                    1                      0
C19-CB-0002           1                    1                      0
C19-CB-0005           1                    1                      0
C19-CB-0009           1                    0                      1
C19-CB-0012           1                    0                      1
C19-CB-0013           1                    0                      1
C19-CB-0011           1                    0                      1
C19-CB-0008           1                    0                      1
C19-CB-0020           1                    0                      1
C19-CB-0021           1                    0                      1
C19-CB-0016           1                    0                      1
C19-CB-0198           1                    0                      1
C19-CB-0204           1                    1                      0
C19-CB-0199           1                    0                      1
C19-CB-0214           1                    1                      0
C19-CB-0053           1                    1                      0
C19-CB-0052           1                    1                      0
P18F                  1                    0                      0
P17H                  1                    0                      0
P20H                  1                    0                      0
P15F                  1                    0                      0
P08H                  1                    0                      0
P13H                  1                    0                      0
P07H                  1                    0                      0
P06F                  1                    0                      0
P04H                  1                    0                      0
C2P01H                1                    0                      0
P09H                  1                    0                      0
P02H                  1                    0                      0
C2P05F                1                    0                      0
C2P07H                1                    0                      0
C2P13F                1                    0                      0
C2P16H                1                    0                      0
C2P10H                1                    0                      0
C2P19H                1                    0                      0
C2P15H                1                    0                      0
one_k_v3              1                    0                      0
Five_k_v3             1                    0                      0
Ten_k_v3              1                    0                      0
DiffExp <- getDiffExp.gedi( model, c(0,0,1) )
meta$velocity_severe<- colSums(DiffExp^2)

Estimate Dot Product and Cosine Similarity

dotprod <- crossprod(DiffExp,gradients) # to get cosine similarity, first calculate dot product

cosineSim <- dotprod / sqrt(colSums(DiffExp^2)) # then, divide by the length of the expression vectors
cosineSim <- t( t(cosineSim) / sqrt(colSums(gradients^2)) ) # and also divide by the length of the

# The gradient vector of each TF is first normalized to have a length of one (by dividing by the Euclidean length of the vector)
gradients_norm<- scale(gradients, center=FALSE, scale=apply(gradients, 2, norm, type="2") )

dotprod_norm <- crossprod(DiffExp,gradients_norm) # to get cosine similarity, first calculate dot product

TF gradient

# Choosing TF
tf<- "SPI1"
set.seed(43)

## TF gradient

C<- model$aux$inputC # Get input C matrix

Cindex <- which(colnames(C)==tf)
vectorField <- svd.joint_vectorField_gradient.gedi(
  model, start.cond = c(1,0,0), end.cond = c(1,0,1), Cindex, scale_cond_vector = 0.5  )
Gradient vectors will be scaled by a factor of 0.000394667923765283.
# Euclidean distance
umap_vectorField <- umap(
  vectorField$v %*% diag(vectorField$d), min_dist=0.5,
  metric="euclidean")
## Cell type embedding indices
ggp<- plot_embedding( umap_vectorField[vectorField$embedding_indices,], meta$id.celltype) +
    theme_void() +
    theme(legend.position ="none")
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.

Warning: Please use tidy evaluation idioms with `aes()`.

Warning: See also `vignette("ggplot2-in-packages")` for more information.
ggp

## Saving the colors
g <- ggplot_build(ggp)

df<- g$data[[1]]
df<- unique(df[,c("group", "colour")])
df<- df[order(df$group),]
temp_vec<- levels(meta$id.celltype)
temp_vec<- temp_vec[temp_vec %in% unique(meta$id.celltype)]
df$celltype<- temp_vec

vec_colors<- df$colour
names(vec_colors)<- df$celltype


ggp<- plot_embedding( umap_vectorField[vectorField$embedding_indices,], meta$id.celltype) +
    theme_void() +
    theme(legend.position ="right")

legend <- cowplot::get_legend(ggp)

grid.newpage()
grid.draw(legend)

## covid vector field with the speed 
ggp<- plot_vectorField( umap_vectorField[vectorField$vectorField_indices,], meta$velocity, minNum=15 ) +
    theme_void() +
    labs(title="Vector field of severe COVID-19") +
    theme(legend.position ="right")

ggp

## TF gradient with TF activity
ggp<- plot_vectorField( umap_vectorField[vectorField$gradient_indices,], ADB[tf,], minNum=15 ) +
    theme_void() +
    labs(title=paste0("TF activity:", tf)) +
    theme(legend.position ="right")

ggp

## UMAP plot with TF activity
ggp<- plot_embedding( umap_vectorField[vectorField$embedding_indices,], ADB[tf,]) +
    theme_void() +
    labs(title=paste0("TF activity:", tf)) +    
    theme(legend.position="right")

ggp

sessionInfo()
R version 4.0.0 (2020-04-24)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /cvmfs/soft.computecanada.ca/easybuild/software/2020/Core/imkl/2020.1.217/compilers_and_libraries_2020.1.217/linux/mkl/lib/intel64_lin/libmkl_gf_lp64.so

locale:
 [1] LC_CTYPE=en_CA.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_CA.UTF-8        LC_COLLATE=en_CA.UTF-8    
 [5] LC_MONETARY=en_CA.UTF-8    LC_MESSAGES=en_CA.UTF-8   
 [7] LC_PAPER=en_CA.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_CA.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
 [1] grid      parallel  stats4    stats     graphics  grDevices utils    
 [8] datasets  methods   base     

other attached packages:
 [1] GEDI_0.0.0.9000             gridExtra_2.3              
 [3] uwot_0.1.10                 RColorBrewer_1.1-2         
 [5] pheatmap_1.0.12             HDF5Array_1.18.1           
 [7] rhdf5_2.34.0                DelayedArray_0.16.3        
 [9] Matrix_1.3-3                scater_1.18.6              
[11] ggplot2_3.4.2               scran_1.18.6               
[13] SingleCellExperiment_1.12.0 SummarizedExperiment_1.20.0
[15] Biobase_2.50.0              GenomicRanges_1.42.0       
[17] GenomeInfoDb_1.26.7         IRanges_2.24.1             
[19] S4Vectors_0.28.1            BiocGenerics_0.36.0        
[21] MatrixGenerics_1.2.1        matrixStats_0.58.0         

loaded via a namespace (and not attached):
 [1] bitops_1.0-6              RcppAnnoy_0.0.18         
 [3] backports_1.2.1           tools_4.0.0              
 [5] bslib_0.2.4               utf8_1.2.1               
 [7] R6_2.5.0                  irlba_2.3.3              
 [9] vipor_0.4.5               colorspace_2.0-0         
[11] rhdf5filters_1.2.0        withr_2.5.0              
[13] tidyselect_1.2.0          compiler_4.0.0           
[15] cli_3.6.1                 BiocNeighbors_1.8.2      
[17] labeling_0.4.2            sass_0.3.1               
[19] checkmate_2.1.0           scales_1.2.1             
[21] metR_0.13.0               stringr_1.5.0            
[23] digest_0.6.27             rmarkdown_2.7            
[25] XVector_0.30.0            pkgconfig_2.0.3          
[27] htmltools_0.5.1.1         sparseMatrixStats_1.2.1  
[29] highr_0.8                 fastmap_1.1.0            
[31] limma_3.46.0              rlang_1.1.0              
[33] DelayedMatrixStats_1.12.3 farver_2.1.0             
[35] jquerylib_0.1.3           generics_0.1.3           
[37] jsonlite_1.8.4            BiocParallel_1.24.1      
[39] dplyr_1.1.1               RCurl_1.98-1.3           
[41] magrittr_2.0.3            BiocSingular_1.6.0       
[43] GenomeInfoDbData_1.2.4    scuttle_1.0.4            
[45] Rcpp_1.0.8.3              ggbeeswarm_0.6.0         
[47] munsell_0.5.0             Rhdf5lib_1.12.1          
[49] fansi_0.4.2               viridis_0.5.1            
[51] lifecycle_1.0.3           stringi_1.5.3            
[53] yaml_2.2.1                edgeR_3.32.1             
[55] zlibbioc_1.36.0           plyr_1.8.6               
[57] dqrng_0.2.1               lattice_0.20-41          
[59] cowplot_1.1.1             beachmat_2.6.4           
[61] locfit_1.5-9.4            knitr_1.31               
[63] pillar_1.8.1              igraph_1.3.4             
[65] codetools_0.2-16          glue_1.6.2               
[67] evaluate_0.14             data.table_1.14.0        
[69] vctrs_0.6.1               gtable_0.3.0             
[71] cachem_1.0.4              xfun_0.22                
[73] rsvd_1.0.5                RcppEigen_0.3.3.9.1      
[75] RSpectra_0.16-0           viridisLite_0.3.0        
[77] tibble_3.2.1              memoise_2.0.0            
[79] beeswarm_0.3.1            bluster_1.0.0            
[81] statmod_1.4.35           
LS0tCnRpdGxlOiAiQ09WSUQtMTkgY29ob3J0MSBURiBncmFkaWVudCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgojIExvYWRpbmcgbGlicmFyaWVzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KCmxpYnJhcnkoc2NyYW4pCmxpYnJhcnkoc2NhdGVyKQpsaWJyYXJ5KEhERjVBcnJheSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkodXdvdCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShHRURJKQoKYGBgCgojIERpcmVjdG9yaWVzCgpgYGB7cn0KCmRpcl9kYXRhPC0gIi4uL2RhdGFfb2JqZWN0cy8iCmRpcl9kYXRhX2hkZjU8LSBwYXN0ZTAoZGlyX2RhdGEsICJDT1ZJRDE5X1NDRS8iKQoKYGBgCgojIEZ1bmN0aW9ucwoKYGBge3J9CgojJyBQbG90IDJEIGVtYmVkZGluZyAKIycKIycgUGxvdCBhIDJEIHJlcHJlc2VudGF0aW9uIChlbWJlZGRpbmcpIG9mIGNlbGxzCiMnIEBwYXJhbSBlbWJlZGRpbmdfbWF0IEVtYmVkZGluZwojJyBAcGFyYW0gY29sb3VyIHZlY3RvciBvZiB2YXJpYWJsZSB0byBwbG90CiMnIEBwYXJhbSByYW5kb21pemUgTG9naWNhbC4gV2hldGhlciB0byByYW5kb21pemUgZGF0YSBiZWZvcmUgcGxvdHRpbmcuCiMnCiMnIEByZXR1cm4gZ2dwbG90MiBvYmplY3QKIycgQGV4cG9ydAojJwpwbG90X2VtYmVkZGluZyA8LSBmdW5jdGlvbihlbWJlZGRpbmdfbWF0LGNvbG91cixyYW5kb21pemU9VCwgc2l6ZV9wb2ludD0wLjA1KSB7CiAgIyBjcmVhdGUgYSBkYXRhIGZyYW1lIHRoYXQgd2lsbCBoYXZlIHRoZSBlbWJlZGRpbmcgYXMgd2VsbCBhcyB0aGUgY29sb3JzCiAgZW1iZWRkaW5nX29iaiA8LSBkYXRhLmZyYW1lKAogICAgRGltMT1lbWJlZGRpbmdfbWF0WywxXSwKICAgIERpbTI9ZW1iZWRkaW5nX21hdFssMl0sCiAgICBWYXI9Y29sb3VyICkKCiAgIyByYW5kb21pemUgdGhlIG9yZGVyIG9mIHRoZSBvYmplY3RzCiAgaWYoIHJhbmRvbWl6ZSApIHsKICAgIGVtYmVkZGluZ19vYmogPC0gZW1iZWRkaW5nX29ialsgc2FtcGxlLmludChucm93KGVtYmVkZGluZ19vYmopKSwgXQogIH0KICAjIGNyZWF0ZSB0aGUgcGxvdHMKICBpZihpcy5udW1lcmljKGNvbG91cikpICMgdGhlIGNvbG9yIHZhcmlhYmxlIGlzIG51bWVyaWMKICB7CiAgICAjZW1iZWRkaW5nX29iaiRWYXIgPC0gZW1iZWRkaW5nX29iaiRWYXIgLSBtZWFuKGVtYmVkZGluZ19vYmokVmFyKQogICAgbGltIDwtIHN0YXRzOjpxdWFudGlsZShhYnMoZW1iZWRkaW5nX29iaiRWYXIpLDAuOTkpCiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoCiAgICAgIGVtYmVkZGluZ19vYmosIGdncGxvdDI6OmFlc19zdHJpbmcoIHg9IkRpbTEiLCB5PSJEaW0yIiwgY29sb3VyPSJWYXIiKSkrCiAgICAgIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZT1zaXplX3BvaW50KSsKICAgICAgZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpKwogICAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oIGxpbWl0cz1jKC1saW0sbGltKSwgY29sb3Vycz1jKCJibHVlIiwibGlnaHQgZ3JleSIsInJlZCIpLCBvb2I9c2NhbGVzOjpzcXVpc2ggKQogIH0gZWxzZSB7CiAgICBnZ3Bsb3QyOjpnZ3Bsb3QoCiAgICAgIGVtYmVkZGluZ19vYmosIGdncGxvdDI6OmFlc19zdHJpbmcoIHg9IkRpbTEiLCB5PSJEaW0yIiwgY29sb3VyPSJWYXIiKSkrCiAgICAgIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZT1zaXplX3BvaW50KSsKICAgICAgZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpKwogICAgICBnZ3Bsb3QyOjpndWlkZXMoY29sb3VyPWdncGxvdDI6Omd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9bGlzdChzaXplPTMpKSkKCiAgfQp9CgpzdW1tYXJ5X21hcmtlcnM8LSBmdW5jdGlvbihtYXJrZXJzX3NjZSl7CiAgICBsaXNfbWFya2VyczwtIGxhcHBseShuYW1lcyhtYXJrZXJzX3NjZSksIGZ1bmN0aW9uKGNsdXN0ZXJfbG9vaykgewogICAgICAgIHRlbXBfZGY8LSBtYXJrZXJzX3NjZVtbY2x1c3Rlcl9sb29rXV0KICAgICAgICB0ZW1wX2RmJEdlbmU8LSByb3duYW1lcyh0ZW1wX2RmKQogICAgICAgIHRlbXBfZGYkY2x1c3RlcjwtIGNsdXN0ZXJfbG9vawogICAgICAgICMgQ2hlY2tpbmcgaWYgYW4gRkRSIGlzIGVxdWFsIHRvIDAKICAgICAgICBpZiggYW55KHRlbXBfZGYkRkRSID09IDApICl7CiAgICAgICAgICAgIG1pbl9wdmFsdWU8LSBzb3J0KHVuaXF1ZSh0ZW1wX2RmJEZEUikpWzJdICMgdGFrZSB0aGUgc2Vjb25kIGxvd2VzdCBwdmFsdWUKICAgICAgICAgICAgY2F0KCJwdmFsdWVzIGVxdWFsIHRvIDAgaGF2ZSBiZWVuIGNoYW5nZWQgdG86IiwgbWluX3B2YWx1ZSwgIlxuIikKICAgICAgICAgICAgdGVtcF9kZiRGRFJbdGVtcF9kZiRGRFIgPT0gMF0gPC0gbWluX3B2YWx1ZSAjIHNldHRpbmcgbG93ZXIgYm91bmQKICAgICAgICB9CiAgICAgICAgdGVtcF9kZiRuZWdsb2dfRkRSPC0gLWxvZzEwKHRlbXBfZGYkRkRSKQogICAgICAgIHRlbXBfZGY8LSB0ZW1wX2RmW3NvcnQocm93bmFtZXModGVtcF9kZikpLF0KICAgICAgICBkYXRhLmZyYW1lKHRlbXBfZGZbLGMoIkdlbmUiLCAiY2x1c3RlciIsICJwLnZhbHVlIiwgIkZEUiIsICJuZWdsb2dfRkRSIildKQogICAgfSkKICAgIG5hbWVzKGxpc19tYXJrZXJzKTwtIG5hbWVzKG1hcmtlcnNfc2NlKSAgICAKICAgIHJldHVybihsaXNfbWFya2VycykgICAgCn0KCnBoZWF0bWFwLmNvbG9yc3ltbWV0cmljIDwtIGZ1bmN0aW9uKHgsbGltPU5VTEwsLi4uKQp7CiAgICByZXF1aXJlKHBoZWF0bWFwKQogICAgaWYoaXMubnVsbChsaW0pICl7CiAgICAgICAgbGltIDwtIG1heChhYnMoeCksIG5hLnJtPVRSVUUpCiAgICB9CiAgICBpZiggbWluKHgsIG5hLnJtPVRSVUUpIDwgMCApewogICAgICAgIGxpbV9kb3duPC0gLWxpbQogICAgICAgIGNvbF9wYWxldHRlPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwid2hpdGUiLCJyZWQiKSkoMjU2KQogICAgfWVsc2V7ICAgICAgICAKICAgICAgICBsaW1fZG93bjwtIDAKICAgICAgICBjb2xfcGFsZXR0ZTwtIGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJyZWQiKSkoMjU2KQogICAgfQogICAgcGhlYXRtYXAoCiAgICAgICAgeCwgY29sb3IgPSBjb2xfcGFsZXR0ZSwKICAgICAgICBicmVha3M9c2VxKGxpbV9kb3duLGxpbSxsZW5ndGgub3V0PTI1NSksIC4uLiApCn0KCmBgYAoKIyBMb2FkaW5nIGRhdGEKCmBgYHtyfQoKIyBSZWFkaW5nIFNDRSBvYmplY3QKc2NlPC0gbG9hZEhERjVTdW1tYXJpemVkRXhwZXJpbWVudChkaXI9ZGlyX2RhdGFfaGRmNSkKCmBgYAoKIyBMb2FkaW5nIEdFREkgbW9kZWwKCmBgYHtyfQoKbWV0YTwtIGRhdGEuZnJhbWUoY29sRGF0YShzY2UpKQoKIyBSZWFkaW5nIEdFREkgbW9kZWwgCm1vZGVsPC0gcmVhZFJEUyhwYXN0ZTAoZGlyX2RhdGEsICJDT1ZJRDE5X2dlZGlfbW9kZWxfY29ob3J0MV9URi5yZHMiKSkKICAgIAojIHJlb3JkZXIgbWV0YSBiYXNlZCBvbiBHRURJIG9yZGVyCm1ldGE8LSBtZXRhW21vZGVsJGF1eCRjZWxsSURzLF0KCiMgR2V0IGFjdGl2aXRpZXMgcGVyIGNlbGwKQURCPC0gZ2V0QURCLmdlZGkobW9kZWwpCgojIEdldCBaREIKWkRCPC0gZ2V0WkRCLmdlZGkobW9kZWwpCgojIEdldCB0aGUgZ3JhZGllbnQgZm9yIGFsbCBURnMKZ3JhZGllbnRzIDwtIGdldEFjdGl2aXR5R3JhZGllbnRzLmdlZGkoIG1vZGVsICkKCiMgTm93LCByZXRyaWV2ZSB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBwZXIgY2VsbCAoIHNldmVyZSB2cyBjb250cm9sKQp0KCBtb2RlbCRhdXgkaW5wdXRIKQoKRGlmZkV4cCA8LSBnZXREaWZmRXhwLmdlZGkoIG1vZGVsLCBjKDAsMCwxKSApCm1ldGEkdmVsb2NpdHlfc2V2ZXJlPC0gY29sU3VtcyhEaWZmRXhwXjIpCgoKYGBgCgpFc3RpbWF0ZSBEb3QgUHJvZHVjdCBhbmQgQ29zaW5lIFNpbWlsYXJpdHkKCgpgYGB7cn0KCmRvdHByb2QgPC0gY3Jvc3Nwcm9kKERpZmZFeHAsZ3JhZGllbnRzKSAjIHRvIGdldCBjb3NpbmUgc2ltaWxhcml0eSwgZmlyc3QgY2FsY3VsYXRlIGRvdCBwcm9kdWN0Cgpjb3NpbmVTaW0gPC0gZG90cHJvZCAvIHNxcnQoY29sU3VtcyhEaWZmRXhwXjIpKSAjIHRoZW4sIGRpdmlkZSBieSB0aGUgbGVuZ3RoIG9mIHRoZSBleHByZXNzaW9uIHZlY3RvcnMKY29zaW5lU2ltIDwtIHQoIHQoY29zaW5lU2ltKSAvIHNxcnQoY29sU3VtcyhncmFkaWVudHNeMikpICkgIyBhbmQgYWxzbyBkaXZpZGUgYnkgdGhlIGxlbmd0aCBvZiB0aGUKCiMgVGhlIGdyYWRpZW50IHZlY3RvciBvZiBlYWNoIFRGIGlzIGZpcnN0IG5vcm1hbGl6ZWQgdG8gaGF2ZSBhIGxlbmd0aCBvZiBvbmUgKGJ5IGRpdmlkaW5nIGJ5IHRoZSBFdWNsaWRlYW4gbGVuZ3RoIG9mIHRoZSB2ZWN0b3IpCmdyYWRpZW50c19ub3JtPC0gc2NhbGUoZ3JhZGllbnRzLCBjZW50ZXI9RkFMU0UsIHNjYWxlPWFwcGx5KGdyYWRpZW50cywgMiwgbm9ybSwgdHlwZT0iMiIpICkKCmRvdHByb2Rfbm9ybSA8LSBjcm9zc3Byb2QoRGlmZkV4cCxncmFkaWVudHNfbm9ybSkgIyB0byBnZXQgY29zaW5lIHNpbWlsYXJpdHksIGZpcnN0IGNhbGN1bGF0ZSBkb3QgcHJvZHVjdAoKCmBgYAoKCiMjIFRGIGdyYWRpZW50CgpgYGB7cn0KCiMgQ2hvb3NpbmcgVEYKdGY8LSAiU1BJMSIKCmBgYAoKYGBge3J9CgpzZXQuc2VlZCg0MykKCiMjIFRGIGdyYWRpZW50CgpDPC0gbW9kZWwkYXV4JGlucHV0QyAjIEdldCBpbnB1dCBDIG1hdHJpeAoKQ2luZGV4IDwtIHdoaWNoKGNvbG5hbWVzKEMpPT10ZikKdmVjdG9yRmllbGQgPC0gc3ZkLmpvaW50X3ZlY3RvckZpZWxkX2dyYWRpZW50LmdlZGkoCiAgbW9kZWwsIHN0YXJ0LmNvbmQgPSBjKDEsMCwwKSwgZW5kLmNvbmQgPSBjKDEsMCwxKSwgQ2luZGV4LCBzY2FsZV9jb25kX3ZlY3RvciA9IDAuNSAgKQoKIyBFdWNsaWRlYW4gZGlzdGFuY2UKdW1hcF92ZWN0b3JGaWVsZCA8LSB1bWFwKAogIHZlY3RvckZpZWxkJHYgJSolIGRpYWcodmVjdG9yRmllbGQkZCksIG1pbl9kaXN0PTAuNSwKICBtZXRyaWM9ImV1Y2xpZGVhbiIpCgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03fQoKIyMgQ2VsbCB0eXBlIGVtYmVkZGluZyBpbmRpY2VzCmdncDwtIHBsb3RfZW1iZWRkaW5nKCB1bWFwX3ZlY3RvckZpZWxkW3ZlY3RvckZpZWxkJGVtYmVkZGluZ19pbmRpY2VzLF0sIG1ldGEkaWQuY2VsbHR5cGUpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSJub25lIikKCmdncAoKIyMgU2F2aW5nIHRoZSBjb2xvcnMKZyA8LSBnZ3Bsb3RfYnVpbGQoZ2dwKQoKZGY8LSBnJGRhdGFbWzFdXQpkZjwtIHVuaXF1ZShkZlssYygiZ3JvdXAiLCAiY29sb3VyIildKQpkZjwtIGRmW29yZGVyKGRmJGdyb3VwKSxdCnRlbXBfdmVjPC0gbGV2ZWxzKG1ldGEkaWQuY2VsbHR5cGUpCnRlbXBfdmVjPC0gdGVtcF92ZWNbdGVtcF92ZWMgJWluJSB1bmlxdWUobWV0YSRpZC5jZWxsdHlwZSldCmRmJGNlbGx0eXBlPC0gdGVtcF92ZWMKCnZlY19jb2xvcnM8LSBkZiRjb2xvdXIKbmFtZXModmVjX2NvbG9ycyk8LSBkZiRjZWxsdHlwZQoKCmdncDwtIHBsb3RfZW1iZWRkaW5nKCB1bWFwX3ZlY3RvckZpZWxkW3ZlY3RvckZpZWxkJGVtYmVkZGluZ19pbmRpY2VzLF0sIG1ldGEkaWQuY2VsbHR5cGUpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSJyaWdodCIpCgpsZWdlbmQgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChnZ3ApCgpncmlkLm5ld3BhZ2UoKQpncmlkLmRyYXcobGVnZW5kKQoKCiMjIGNvdmlkIHZlY3RvciBmaWVsZCB3aXRoIHRoZSBzcGVlZCAKZ2dwPC0gcGxvdF92ZWN0b3JGaWVsZCggdW1hcF92ZWN0b3JGaWVsZFt2ZWN0b3JGaWVsZCR2ZWN0b3JGaWVsZF9pbmRpY2VzLF0sIG1ldGEkdmVsb2NpdHksIG1pbk51bT0xNSApICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICBsYWJzKHRpdGxlPSJWZWN0b3IgZmllbGQgb2Ygc2V2ZXJlIENPVklELTE5IikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0icmlnaHQiKQoKZ2dwCgojIyBURiBncmFkaWVudCB3aXRoIFRGIGFjdGl2aXR5CmdncDwtIHBsb3RfdmVjdG9yRmllbGQoIHVtYXBfdmVjdG9yRmllbGRbdmVjdG9yRmllbGQkZ3JhZGllbnRfaW5kaWNlcyxdLCBBREJbdGYsXSwgbWluTnVtPTE1ICkgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIGxhYnModGl0bGU9cGFzdGUwKCJURiBhY3Rpdml0eToiLCB0ZikpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9InJpZ2h0IikKCmdncAoKCiMjIFVNQVAgcGxvdCB3aXRoIFRGIGFjdGl2aXR5CmdncDwtIHBsb3RfZW1iZWRkaW5nKCB1bWFwX3ZlY3RvckZpZWxkW3ZlY3RvckZpZWxkJGVtYmVkZGluZ19pbmRpY2VzLF0sIEFEQlt0ZixdKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgbGFicyh0aXRsZT1wYXN0ZTAoIlRGIGFjdGl2aXR5OiIsIHRmKSkgKyAgICAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKQoKZ2dwCgpgYGAKCgoKYGBge3J9CgpzZXNzaW9uSW5mbygpCgpgYGAK